package com.dsp.soundscape.client;

import java.util.ArrayList;
import java.util.List;

import com.dsp.soundscape.client.widgets.AlbumDialog;
import com.dsp.soundscape.model.AlbumInfo;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

public class Soundscape implements EntryPoint {
	
	int failed = 0;
	int returned = 0;
	int requested = 0;
	
	Anchor yearAnchor;
	Anchor artistAnchor;
	
	List<String> allArtists;
	List<String> allYears;
	
	List<String> albumIds = new ArrayList<String>();
	
	ArtistDialog artistDialog;
	YearDialog yearDialog;
	WaitDialog waitDialog;
	
	@Override
	public void onModuleLoad() {
		RootPanel yearContainer = RootPanel.get("selectedYearContainer");
		String yearSelected = DOM.getElementById("selectedYear").getInnerText();
		yearContainer.getElement().setInnerHTML("");
		
		yearAnchor = new Anchor();
		yearAnchor.setText(yearSelected);
		yearAnchor.setStyleName("pleaseTouchMe");
		yearAnchor.getElement().setId("selectedYear");
		yearContainer.add(yearAnchor);
		yearAnchor.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				yearDialog = null;
				yearDialog = new YearDialog();
			}
		});
		
		RootPanel artistContainer = RootPanel.get("selectedArtistContainer");
		String artistSelected = DOM.getElementById("selectedArtist").getInnerText();
		artistContainer.getElement().setInnerHTML("");
		
		artistAnchor = new Anchor();
		artistAnchor.setText(artistSelected);
		artistAnchor.setStyleName("pleaseTouchMe");
		artistAnchor.getElement().setId("selectedArtist");
		artistContainer.add(artistAnchor);
		artistAnchor.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				artistDialog = null;
				artistDialog = new ArtistDialog();
			}
		});
		
		RootPanel randomContainer = RootPanel.get("theRandomizer");
		String randomText = DOM.getElementById("randomLink").getInnerText();
		randomContainer.getElement().setInnerHTML("");
		
		Anchor randomAnchor = new Anchor();
		randomAnchor.setText(randomText);
		randomAnchor.setStyleName("pleaseTouchMe");
		randomContainer.add(randomAnchor);
		
		randomAnchor.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				showWaitDialog();
				ServiceLocator.getSoundscapeService().randomize(new AsyncCallback<List<AlbumInfo>>(){

					@Override
					public void onFailure(Throwable caught) {
						// TODO O Noes!
					}

					@Override
					public void onSuccess(List<AlbumInfo> result) {
						renderNewList(result);
						clearValue(artistAnchor);
						clearValue(yearAnchor);
						waitDialog.hide();
					}
				});
			}
		});
		
		Element element = DOM.getElementById("theContents");
		for (Element child : DomUtil.findChildren(element, "td", "thumbnail")) {
			albumIds.add(child.getId());
			requested ++;
		}
		
		Timer dialogPreloadTimer = new Timer() {
			@Override
			public void run() {
				startDialogSetup();
			}	
		};
		
		dialogPreloadTimer.schedule(10);
		startAlbumInfoTimer();
	}
	
	private void startDialogSetup() {				
		ServiceLocator.getSoundscapeService().getYears(new AsyncCallback<List<String>>() {
			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub
			}
	
			@Override
			public void onSuccess(List<String> result) {
				allYears = result;
			}
		});
		
		ServiceLocator.getSoundscapeService().getArtists(new AsyncCallback<List<String>>() {

			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub
			}
			
			@Override
			public void onSuccess(List<String> result) {
				allArtists = result;
			}
		});
	}
	
	private class WaitDialog extends DialogBox {
		public WaitDialog() {
			FlowPanel spinnerPanel = new FlowPanel();
			spinnerPanel.getElement().setAttribute("align", "center");
			spinnerPanel.add(new Image("images/album-spinner.gif"));
			add(spinnerPanel);
			
			this.setGlassEnabled(true);
			this.setModal(true);
			
			this.setText("Loading");
		}
	}
	
	private abstract class CheckedRenderDialog extends DialogBox {
		protected ListBox selector = new ListBox();
		
		protected abstract List<String> getData();
		protected abstract void render();
		
		protected void checkedRender() {
			if (getData() != null) {
				if (waitDialog != null) {
					waitDialog.hide();
				}
				render();

			} else {
				Timer timer = new Timer() {
					@Override
					public void run() {
						checkedRender();
					}
				};

				timer.schedule(50);
				showWaitDialog();
			}
		}
	}
	
	private class YearDialog extends CheckedRenderDialog {
		public YearDialog() {
			setGlassEnabled(true);
			setModal(true);
			setText("Select Year");
			
			checkedRender();
		}
		
		@Override
		protected List<String> getData() {
			return allYears;
		}
		
		@Override
		protected void render() {
			selector.setWidth("10em");
			selector.getElement().setAttribute("size", String.valueOf(8));

			for (String year : allYears) {
				selector.addItem(year);
				if (year.equals(yearAnchor.getText())) {
					selector.setSelectedIndex(selector.getItemCount() - 1);
				}
			}
			
			Button cancel = new Button("CANCEL");
			cancel.addClickHandler(new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					hide();
				}				
			});
			
			selector.addChangeHandler(new ChangeHandler(){
				@Override
				public void onChange(ChangeEvent event) {
					selectYear(selector.getValue(selector.getSelectedIndex()));
					hide();
				}
			});
			
			VerticalPanel panel = new VerticalPanel();
			panel.add(selector);
			panel.add(cancel);
			add(panel);
			center();
			show();
		}
	}
	
	private class ArtistDialog extends CheckedRenderDialog {
		private TextBox filter;
		
		public ArtistDialog() {
			this.setGlassEnabled(true);
			this.setModal(true);

			this.setText("Select Artist");
			
			filter = new TextBox();
			selector = new ListBox();

			checkedRender();
		}
		
		@Override
		protected List<String> getData() {
			return allArtists;
		}
		
		@Override
		protected void render() {
			selector.getElement().setAttribute("size", String.valueOf(15));
			updateList(allArtists, artistAnchor.getText());

			Button cancel = new Button("CANCEL");
			cancel.addClickHandler(new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					hide();
				}				
			});
			
			selector.addChangeHandler(new ChangeHandler(){
				@Override
				public void onChange(ChangeEvent event) {
					selectArtist(selector.getValue(selector.getSelectedIndex()));		
					hide();
				}
			});
			
			filter.addKeyUpHandler(new KeyUpHandler() {
	
				@Override
				public void onKeyUp(KeyUpEvent event) {
					if (filter.getText().length() > 0) {
						List <String> matches = new ArrayList<String>();
						for (String artist : allArtists) {
							if (artist.toLowerCase().contains(filter.getText().toLowerCase())) {
								matches.add(artist);
							}
						}
						updateList(matches, null);
					} else {
						updateList(allArtists, null);
					}
				}
				
			});
			
			VerticalPanel panel = new VerticalPanel();
			panel.add(filter);
			panel.add(selector);
			panel.add(cancel);
			add(panel);
			center();
			show();
			
			int width = selector.getOffsetWidth() + 10;
			filter.setWidth(width + "px");
			selector.setWidth(width + "px");
		}

		private void updateList(List<String> result, String value) {
			selector.setSelectedIndex(-1);
			selector.clear();
			for (String artist : result) {
				selector.addItem(artist);
				if (value != null && value.equals(artist)) {
					selector.setSelectedIndex(selector.getItemCount() - 1);
				}
			}
		}
	}
	
	private class AlbumInfoCallback implements AsyncCallback <AlbumInfo> {
		private String id;
		
		public AlbumInfoCallback(String id) {
			this.id = id;
		}
		
		@Override
		public void onFailure(Throwable caught) {
			RootPanel.get(id).removeFromParent();
			returned ++;
			failed ++;
			
			ifCompleted();
		}

		@Override
		public void onSuccess(AlbumInfo result) {
			attachClickHandlers(result);
			returned ++;
			
			ifCompleted();
		}
	}
	
	private class AlbumClickHandler implements ClickHandler {
		private AlbumInfo info;
		
		public AlbumClickHandler(AlbumInfo info) {
			this.info = info;
		}
		
		@Override
		public void onClick(ClickEvent event) {
			AlbumDialog dialog = new AlbumDialog(info);
			dialog.show();
		}
	}
	
	private class ArtistNameClickHandler implements ClickHandler {
		private String artist;
		
		public ArtistNameClickHandler(String artist) {
			this.artist = artist;
		}

		@Override
		public void onClick(ClickEvent event) {
			selectArtist(artist);
		}
	}

	private class YearClickHandler implements ClickHandler {
		private String year;
		
		public YearClickHandler(String year) {
			this.year = year;
		}

		@Override
		public void onClick(ClickEvent event) {
			selectYear(year);
		}
	}
	
	private void clearValue(Anchor anchor) {
		anchor.setText("----");
	}
	
	private void selectYear(String year) {
		clearValue(artistAnchor);
		yearAnchor.setText(year);
		showWaitDialog();
		
		ServiceLocator.getSoundscapeService().getAlbumsByYear(year,	new AsyncCallback<List<AlbumInfo>>(){
			
			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub
			}

			@Override
			public void onSuccess(List<AlbumInfo> result) {
				renderNewList(result);
				waitDialog.hide();
			}
		});
	}

	private void selectArtist(String artist) {
		clearValue(yearAnchor);
		artistAnchor.setText(artist);
		showWaitDialog();
		
		ServiceLocator.getSoundscapeService().getAlbumsByArtist(artist, new AsyncCallback<List<AlbumInfo>>(){
			
			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub
			}

			@Override
			public void onSuccess(List<AlbumInfo> result) {
				renderNewList(result);
				waitDialog.hide();
			}
		});
	}
	
	private void startAlbumInfoTimer() {
		Timer timer = new Timer() {
			@Override
			public void run() {
				getAlbumInfo();
			}
		};
		
		timer.schedule(5);
	}
	
	private void getAlbumInfo() {
		if (albumIds.size() > 0) {
			String id = albumIds.get(0);
			albumIds.remove(0);
			ServiceLocator.getSoundscapeService().getInfo(id, new AlbumInfoCallback(id));

			if (albumIds.size() > 0) {
				startAlbumInfoTimer();
			}
		}
	}
	
	private void ifCompleted() {
		if (returned == requested) {
			//TODO
		}
	}
	
	private void showWaitDialog() {
		if (waitDialog == null) {
			waitDialog = new WaitDialog();
		}
		waitDialog.show();
		waitDialog.center();
	}
	
	private void renderNewList (List<AlbumInfo> info) {
		Element container = DOM.getElementById("theContents");
		Element albumTable = DomUtil.findChild(container, "table", "albuminfo");
		
		albumTable.removeFromParent();
		container.setInnerHTML("");
		
		requested = 0;
		
		for (AlbumInfo album : info) {
			Element newChild = (Element) albumTable.cloneNode(true).cast();
			DomUtil.findChild(newChild, "td", "thumbnail").setId(album.getId());
			DomUtil.findChild(newChild, "span", "albumName").setInnerText(album.getAlbum());
			DomUtil.findChild(newChild, "span", "artistName").setId("link_" + album.getId());
			DomUtil.findChild(newChild, "a", "artistLink").setInnerText(album.getArtist());
			DomUtil.findChild(newChild, "span", "year").setId("year_" + album.getId());
			DomUtil.findChild(newChild, "a", "whiteLink").setInnerText(album.getYear());
			ImageElement img = (ImageElement) DomUtil.findChild(newChild, "img", "thumbnail-image").cast();
			img.removeClassName("thumbnail-image");
			img.setSrc("images/album-spinner.gif");
			container.appendChild(newChild);
			albumIds.add(album.getId());
			requested++;
		}
		
		startAlbumInfoTimer();
	}
	
	private void attachClickHandlers(AlbumInfo info) {
		Image image;
		if (info.isImageAvailable()) {
			image = new Image(info.getThumb());
		} else {
			image = new Image("images/noart.jpg");
		}
		
		image.addClickHandler(new AlbumClickHandler(info));
		image.setStyleName("thumbnail-image");
		image.addStyleName("pleaseTouchMe");
		
		Element imageContainer = DOM.getElementById(info.getId());
		imageContainer.setInnerHTML("");
		RootPanel.get(info.getId()).add(image);
		
		Element artistElement = DOM.getElementById("link_" + info.getId());
		Element artistHref = DomUtil.findChild(artistElement, "a", "artistLink");
		String artistValue = artistHref.getInnerText();
		artistHref.removeFromParent();
		
		RootPanel artistRoot = RootPanel.get("link_" + info.getId());
		Anchor albumArtist = new Anchor();
		albumArtist.setStyleName("pleaseTouchMe");
		albumArtist.addStyleName("artistLink");
		albumArtist.setText(artistValue);
		albumArtist.addClickHandler(new ArtistNameClickHandler(artistValue));
		artistRoot.add(albumArtist);
		
		Element yearElement = DOM.getElementById("year_" + info.getId());
		Element yearHref = DomUtil.findChild(yearElement, "a", "whiteLink");
		String yearValue = yearHref.getInnerText();
		yearHref.removeFromParent();
		
		RootPanel yearRoot = RootPanel.get("year_" + info.getId());
		Anchor albumYear = new Anchor();
		albumYear.addStyleName("whiteLink");
		albumYear.setText(yearValue);
		albumYear.addClickHandler(new YearClickHandler(yearValue));
		yearRoot.add(albumYear);
		
	}
	
}
